﻿/*
 * Сделано в SharpDevelop.
 * Пользователь: Баданин М.В.
 * Дата: 10.05.2013
 * Время: 15:21
 * Проект: РПН Регион 2013
 * Распространяется под лицензией GPL v3
 */
#define ExportDOCX
using System;
using System.Drawing;
using System.IO;
using System.Xml;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;

namespace Region.Utils
{
  /// <summary>
  /// Description of ExportDOCX.
  /// </summary>
  public class ExportDOCX: ExportOffice
  {
    public ExportDOCX():base("word"){}    
    XmlDocument WordDocument;
    bool replacePicture(XmlElement Element,String find, Image Value)
    {
      var rel = new XmlDocument();
      MEMStream streamJPEG = new MEMStream();
      Value.Save(streamJPEG,System.Drawing.Imaging.ImageFormat.Jpeg);      
      Byte[] Buff = new Byte[streamJPEG.Length];
      streamJPEG.Seek(0,SeekOrigin.Begin);
      streamJPEG.Read(Buff,0,Buff.Length);          
      AddContentType("jpeg","image/jpeg");      
      String FileName;
      for(Int32 i=1;;i++){FileName = "media/image"+i+".jpeg"; if (!m_zip.isFile(FileName)) { break; }}
      String ID = addDocumentRel(Buff,FileName,"http://schemas.openxmlformats.org/officeDocument/2006/relationships/image");                
      String Template=@"<w:drawing><wp:inline distT='0' distB='0' distL='0' distR='0'><wp:extent cx='{WIDTH}' cy='{HEIGHT}'/><wp:effectExtent l='19050' t='0' r='0' b='0'/><wp:docPr id='2' name='Рисунок 2'/><wp:cNvGraphicFramePr><a:graphicFrameLocks xmlns:a='http://schemas.openxmlformats.org/drawingml/2006/main' noChangeAspect='1'/></wp:cNvGraphicFramePr><a:graphic xmlns:a='http://schemas.openxmlformats.org/drawingml/2006/main'><a:graphicData uri='http://schemas.openxmlformats.org/drawingml/2006/picture'><pic:pic xmlns:pic='http://schemas.openxmlformats.org/drawingml/2006/picture'><pic:nvPicPr><pic:cNvPr id='0' name='image.jpg'/><pic:cNvPicPr/></pic:nvPicPr><pic:blipFill><a:blip r:embed='{RID}'><a:extLst><a:ext uri='{28A0092B-C50C-407E-A947-70E740481C1C}'><a14:useLocalDpi xmlns:a14='http://schemas.microsoft.com/office/drawing/2010/main' val='0'/></a:ext></a:extLst></a:blip><a:stretch><a:fillRect/></a:stretch></pic:blipFill><pic:spPr><a:xfrm><a:off x='0' y='0'/><a:ext cx='{WIDTH}' cy='{HEIGHT}'/></a:xfrm><a:prstGeom prst='rect'><a:avLst/></a:prstGeom><a:noFill/><a:ln><a:noFill/></a:ln></pic:spPr></pic:pic></a:graphicData></a:graphic></wp:inline></w:drawing>";

      Template=Template.Replace('\'','"');      
      Template=Template.Replace("{WIDTH}",(Value.Width/Value.HorizontalResolution*914400).ToString());
      Template=Template.Replace("{HEIGHT}",(Value.Height/Value.VerticalResolution*914400).ToString());
      Template=Template.Replace("{RID}",ID);
      //XmlNode node = Element;for(;!(node is XmlDocument);node = node.ParentNode);var doc = (XmlDocument)node;      
      var drawing = WordDocument.CreateElement("w","drawing","");
      var el = Element.Clone();
      el.InnerXml = Template;
            
      return replaceText(Element,find, (XmlElement)el.FirstChild);
    }
    bool replaceText(XmlElement Element, String find, XmlElement replace)
    {
      // Просмотр всех абзацев
      foreach (XmlElement p in Element.GetElementsByTagName("w:p"))
      {
          bool Next = true;
          while(Next)
          {
              Next = false;
              String Str = "";
              List<XmlElement> StrT = new List<XmlElement>();
              foreach (XmlElement r in p.GetElementsByTagName("w:r"))
              {
                  foreach (XmlElement t in r.GetElementsByTagName("w:t"))
                  {                        
                      String S = t.InnerText;
                      Str += S;
                      for (Int32 i = 0; i < S.Length; i++) { StrT.Add(t);}
                  }
              }                                  
              Int32 fi = Str.IndexOf(find);
              if (fi >= 0)
              {
                  Next = true;
                  XmlElement First = StrT[fi];
                  XmlElement Last = StrT[fi + find.Length - 1];
                  Int32 FirstSize = 0;
                  Int32 LastSize = 0;
                  for (Int32 i = fi; i < fi + find.Length; i++)
                  {
                      if (StrT[i] == First) FirstSize++;
                      if (StrT[i] == Last) LastSize++;
                  }
                  if (First == Last)
                  {
                    Int32 Index = Last.InnerText.IndexOf(find);
                    
                    if (Index>0) {XmlElement F = (XmlElement)Last.Clone(); F.InnerText = Last.InnerText.Remove(Index); Last.ParentNode.InsertBefore(F,Last);}
                    //Last.InnerText = "###########";
                    if (Index<Last.InnerText.Length-find.Length) {XmlElement L = (XmlElement)Last.Clone(); L.InnerText = Last.InnerText.Remove(0,Index+find.Length);Last.ParentNode.InsertAfter(L,Last);}
                    Last.ParentNode.ReplaceChild(replace,Last);
                    //p1
                    //First.InnerText = First.InnerText.Replace(find, replace);
                      
                  }
                  else
                  {
                      First.InnerText = First.InnerText.Remove(First.InnerText.Length - FirstSize);
                      
                      Int32 Index = Last.InnerText.IndexOf(find);
                      
                      //Last.InnerText = "###########";
                      

                      for (Int32 i = fi + 1; i < fi + find.Length - 1; i++)
                          if (StrT[i] != First && StrT[i] != Last)
                          {
                              XmlElement f = (XmlElement)StrT[i].ParentNode;
                              p.RemoveChild(StrT[i].ParentNode);
                              for (; StrT[i].ParentNode == f; i++) ;
                          }
                      First.ParentNode.InsertAfter(replace,First);
                      Last.InnerText = Last.InnerText.Remove(0, LastSize);
                      First.ParentNode.InsertAfter(replace,First);                      
                      if (Last.InnerText.Length == 0)
                          p.RemoveChild(Last.ParentNode);
                  }
              }                    
          }
            
        }            
        return true;
    }    
    bool InsertRows(XmlElement Element, String Key, List<Dictionary<String, Object>> Rows)
    {
        foreach (XmlElement tbl in Element.GetElementsByTagName("w:tbl"))
        {
            foreach (XmlElement tr in tbl.GetElementsByTagName("w:tr"))
                if (tr.InnerText.IndexOf(Key)>=0)
            {
                foreach (var Row in Rows)
                {
                    XmlElement newTR = (XmlElement)tr.Clone();
                    foreach (var V in Row)
                    {
                        if (V.Value is String)
                            replaceText(newTR, V.Key, (String)V.Value);
                    }
                    tbl.InsertBefore(newTR, tr);
                }
                tbl.RemoveChild(tr);
                break;
            }
        }
        return true;
    }    
    bool replaceText(XmlElement Element, String find, String replace)
    {
      foreach (XmlElement p in Element.GetElementsByTagName("w:p"))
      {
        bool Next = true;
        while(Next)
        {
          Next = false;
          String Str = "";
          List<XmlElement> StrT = new List<XmlElement>();
          foreach (XmlElement r in p.GetElementsByTagName("w:r"))
          {
            foreach (XmlElement t in r.GetElementsByTagName("w:t"))
            {                        
              String S = t.InnerText;
              Str += S;
              for (Int32 i = 0; i < S.Length; i++) { StrT.Add(t);}
            }
          }                    
          
          Int32 fi = Str.IndexOf(find);
          if (fi >= 0)
          {
            Next = true;
            XmlElement Fisrt = StrT[fi];
            XmlElement Last = StrT[fi + find.Length - 1];
            Int32 FirstSize = 0;
            Int32 LastSize = 0;
            for (Int32 i = fi; i < fi + find.Length; i++)
            {
              if (StrT[i] == Fisrt) FirstSize++;
              if (StrT[i] == Last) LastSize++;
            }
            if (Fisrt == Last)
            {
              //p1
                Fisrt.InnerText = Fisrt.InnerText.Replace(find, replace);
                
            }
            else
            {
              Fisrt.InnerText = Fisrt.InnerText.Remove(Fisrt.InnerText.Length - FirstSize) + replace;
              for (Int32 i = fi + 1; i < fi + find.Length - 1; i++)
                if (StrT[i] != Fisrt && StrT[i] != Last)
                {
                  XmlElement f = (XmlElement)StrT[i].ParentNode;
                  p.RemoveChild(StrT[i].ParentNode);
                  for (; StrT[i].ParentNode == f; i++) ;
                }
              Last.InnerText = Last.InnerText.Remove(0, LastSize);
              if (Last.InnerText.Length == 0)
                p.RemoveChild(Last.ParentNode);
            }
          }                    
        }
          
      }            
      return true;
    }
    bool replace(XmlElement Element,String find, Object Value)
    {
      if (Value is Image) return replacePicture(Element,find,(Image)Value);
      else return replaceText(Element,find,Value.ToString());
    }
    public override bool EditDocument(TaskManager.Task task = null)
    {      
      // Считать документ
      WordDocument = new XmlDocument();
      var XML = Encoding.UTF8.GetString(m_zip.readAll("word/document.xml"));
      WordDocument.LoadXml(XML);
      //Внести изменения
      if (Poles!=null) foreach(var item in Poles) replace(WordDocument.DocumentElement,item.Key, item.Value);
      if (Tables!=null) foreach(var item in Tables) InsertRows(WordDocument.DocumentElement, item.Key,item.Value);
      //Сохранить измения
      m_zip.writeAll("word/document.xml",Encoding.UTF8.GetBytes(WordDocument.InnerXml));
      return true;
    }
  }
}
